#pragma once 
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include "util.hpp"
#include <mutex>

// 独立命名空间
namespace ns_index
{
    // 描述文档的结构体
    struct DocInfo
    {
        std::string title;
        std::string content;
        std::string url;
        uint64_t doc_id;      // 文档id
    };
    // 倒排节点
    struct InvertedElem
    {
        uint64_t doc_id;      // 文档id
        std::string key_word; // 参照的关键字
        int weight;           // 权重
    };
    // 定义倒排拉链
    typedef std::vector<InvertedElem> InvertedList;
    // 单例索引类
    class Index
    {
    private:
        Index(){}
        Index(const Index&)=delete;
        Index& operator=(const Index&)=delete;
        std::vector<DocInfo> forward_index;                     // 正排索引,数组下标天然映射
        std::unordered_map<std::string,InvertedList> inverted_index; // 关键字和文档id的映射
        static Index* instance; // 单例
        static std::mutex mtx;
    public:
        ~Index()
        {}
        // 获取单例
        static Index* GetInstance()
        {
            // 线程安全,加锁
            if(instance==nullptr)
            {
                mtx.lock();
                if(instance==nullptr) instance=new Index();
                mtx.unlock();
            }
            return instance;
        }
        // 文档id找到文档内容
        DocInfo* GetForwardIndex(uint64_t doc_id)
        {
            if(doc_id >= forward_index.size())
            {
                std::cerr<<"doc id out of range, error!"<<std::endl;
                return nullptr;
            }
            return &forward_index[doc_id];
        }
        // 通过关键字,找到倒排拉链
        InvertedList* GetInvertedIndex(const std::string& key_word)
        {
            auto iter=inverted_index.find(key_word);
            if(iter==inverted_index.end())
            {
                std::cerr<<"word: "<<key_word<<" doesn't has a InvertedList!"<<std::endl;
                return nullptr;
            }
            return &iter->second;
        }
        // 构建索引,先构建正排索引,再构建倒排索引,用户则是先倒排搜索,再正排搜索
        // 传入去标签之后的整合文档 "./data/raw_html/raw.txt"
        bool BuildIndex(const std::string& file_path)
        {
            std::ifstream in(file_path,std::ios::in | std::ios::binary);
            if(!in.is_open())
            {
                std::cerr<<"sorry,"<<file_path<<"open faile!"<<std::endl;
                return false;
            }
            std::string line;
            int cnt=0;
            while(std::getline(in,line))
            {
                DocInfo* doc=BuildForwardIndex(line);
                if(doc==nullptr)
                {
                    std::cerr<<"build "<<line<<" error"<<std::endl;
                    continue;
                }
                BuildInvertedIndex(*doc);
                cnt++;
                if(cnt%100==0) std::cout<<"目前已经建立的索引文档："<<cnt<<std::endl;
            }
            return true;
        }
    private: 
        //  构建正排索引
        DocInfo* BuildForwardIndex(const std::string& line)
        {
            // 1、切分字符串
            std::vector<std::string> results;
            const std::string sep="\3";
            ns_util::StringUtil::Split(line,&results,sep);
            if(results.size()!=3) return nullptr;
            // 2、填充DocInfo
            DocInfo doc;
            doc.title=results[0];
            doc.content=results[1];
            doc.url=results[2];
            doc.doc_id=forward_index.size();  // id值与下标映射
            // 3、插入到正排vector
            forward_index.push_back(std::move(doc));
            // return &doc;  doc为临时变量
            return &forward_index.back();
        }
        //  构建倒排索引
        bool BuildInvertedIndex(const DocInfo& doc)
        {
            struct word_cnt
            {
                int title_cnt=0;
                int content_cnt=0;
            };
            std::unordered_map<std::string,word_cnt> word_map; //统计在标题和内容中出现的次数
            // 标题分词,统计该词在这篇中出现的次数
            std::vector<std::string> title_words;
            ns_util::JiebaUtil::CutString(doc.title,&title_words);
            for(auto& str:title_words)
            {
                boost::to_lower(str); // 不区分大小写
                word_map[str].title_cnt++;
            }
            // 内容分词,统计该词在这篇中出现的次数
            std::vector<std::string> content_words;
            ns_util::JiebaUtil::CutString(doc.content,&content_words);
            for(auto& str:content_words)
            {
                boost::to_lower(str); // 不区分大小写
                word_map[str].content_cnt++;
            }
            // 构建倒排元素
            for(auto& iter:word_map) 
            {
                InvertedElem item;
                item.doc_id=doc.doc_id;
                item.key_word=iter.first;
                // 计算权重
                item.weight=iter.second.title_cnt*10+iter.second.content_cnt;      
                inverted_index[item.key_word].push_back(std::move(item)); // 插入倒排节点                                                                                                                                                                                                                                 
            }
            return true;
        }
    };
    Index* Index::instance=nullptr;
    std::mutex Index::mtx;
}